/**
 * @file Muti_MB.c
 * @author Linquan 
 * @brief 
 * @version 0.1
 * @date 2025-07-08
 * 
 * @copyright Copyright (c) 2025
 * 
 * 用于多通信口的modbus slave报文解析框架。支持RTU TCP协议。
 */


#include "Muti_MB_config.h"
#include "string.h"

#include "stdio.h"


/*************** MB TCP  ********************/
#define MB_TCP_TID          0
#define MB_TCP_PID          2
#define MB_TCP_LEN          4
#define MB_TCP_UID          6
#define MB_TCP_FUNC         7

#define MB_TCP_PROTOCOL_ID  0   /* 0 = Modbus Protocol */


eMUTI_MB_ErrorCode
__MUTI_MB_TCP_DataRecv(MUTI_MB_object_t *object, UCHAR * no_used, uint8_t ** ppucFrame, uint16_t * pusLength  );

eMUTI_MB_ErrorCode
__MUTI_MB_TCPSend( MUTI_MB_object_t *object, const UCHAR * pucFrame, USHORT usLength );

/*************** MB TCP  END********************/


/*************** MB RTU  ********************/
#define MB_SER_PDU_SIZE_MIN     4       /*!< Minimum size of a Modbus RTU frame. */
#define MB_SER_PDU_SIZE_MAX     256     /*!< Maximum size of a Modbus RTU frame. */
#define MB_SER_PDU_SIZE_CRC     2       /*!< Size of CRC field in PDU. */
#define MB_SER_PDU_ADDR_OFF     0       /*!< Offset of slave address in Ser-PDU. */
#define MB_SER_PDU_PDU_OFF      1       /*!< Offset of Modbus-PDU in Ser-PDU. */



eMUTI_MB_ErrorCode
__MUTI_MB_RTU_DataRecv(MUTI_MB_object_t *object,  UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength );

eMUTI_MB_ErrorCode
__MUTI_MB_RTU_Send( MUTI_MB_object_t *object, const UCHAR * pucFrame, USHORT usLength );

/*************** MB RTU END ********************/



/**
 * @brief 功能码列表
 * 
 */
static xMUTI_MB_FunctionHandler xFuncHandlers[MUTI_MB_FUNC_HANDLERS_MAX] = {
#if MUTI_MB_FUNC_OTHER_REP_SLAVEID_ENABLED > 0
    {MUTI_MB_FUNC_OTHER_REPORT_SLAVEID, eMUTI_MB_FuncReportSlaveID},
#endif
#if MUTI_MB_FUNC_READ_INPUT_ENABLED > 0
    {MUTI_MB_FUNC_READ_INPUT_REGISTER, eMUTI_MB_FuncReadInputRegister},
#endif
#if MUTI_MB_FUNC_READ_HOLDING_ENABLED > 0
    {MUTI_MB_FUNC_READ_HOLDING_REGISTER, eMUTI_MB_FuncReadHoldingRegister},
#endif
#if MUTI_MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0
    {MUTI_MB_FUNC_WRITE_MULTIPLE_REGISTERS, eMUTI_MB_FuncWriteMultipleHoldingRegister},
#endif
#if MUTI_MB_FUNC_WRITE_HOLDING_ENABLED > 0
    {MUTI_MB_FUNC_WRITE_REGISTER, eMUTI_MB_FuncWriteHoldingRegister},
#endif
#if MUTI_MB_FUNC_READWRITE_HOLDING_ENABLED > 0
    {MUTI_MB_FUNC_READWRITE_MULTIPLE_REGISTERS, eMUTI_MB_FuncReadWriteMultipleHoldingRegister},
#endif
#if MUTI_MB_FUNC_READ_COILS_ENABLED > 0
    {MUTI_MB_FUNC_READ_COILS, eMUTI_MB_FuncReadCoils},
#endif
#if MUTI_MB_FUNC_WRITE_COIL_ENABLED > 0
    {MUTI_MB_FUNC_WRITE_SINGLE_COIL, eMUTI_MB_FuncWriteCoil},
#endif
#if MUTI_MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
    {MUTI_MB_FUNC_WRITE_MULTIPLE_COILS, eMUTI_MB_FuncWriteMultipleCoils},
#endif
#if MUTI_MB_FUNC_READ_DISCRETE_INPUTS_ENABLED > 0
    {MUTI_MB_FUNC_READ_DISCRETE_INPUTS, eMUTI_MB_FuncReadDiscreteInputs},
#endif
};






/*************************************
 * MUTI_MB  API
 * ***********************************/
void MUTI_MB_object_init(MUTI_MB_object_t *object,
						 MUTI_MB_ID_t	 MB_ID,
						 MUTI_MB_handle_ptr     handle_cb,
						 MUTI_MB_send_ptr       send_cb)
{
    
    memset(object->buf, 0, 256);
	object->MB_id    = MB_ID;
    object->state    = IDLE;
    object->active   = ACTIVE;
    object->len      = 0;
    object->protocol_cb = handle_cb; 
    object->send_cb     = send_cb;
}

uint8_t MUTI_MB_data_append(MUTI_MB_object_t *object, 
                        const uint8_t* data, uint16_t len)
{
	uint8_t recv_success = 0;
	if(object->state == IDLE)
	{
		 memcpy(object->buf, data, len);
    
		object->len = len;

		object->state    = RECVED;
		
		recv_success = 1;
	}
	
	return recv_success;
   
}




void MUTI_MB_poll(MUTI_MB_object_t *object)
{
     int             i;
     eMUTI_MB_ErrorCode    eStatus = MUTI_MB_ENOERR;
	 
	
	 uint8_t   *ucMBFrame;
     uint8_t    ucRcvAddress;   
     uint16_t   usLength;
     eMUTI_MB_Exception eException;

    if(object->active == NO_ACTIVE)
    {
        return;
    }

    if(object->state == RECVED)
    {    
		/* 1、 取出接收的数据*/   		
		if(object->MB_id == MUTI_MB_TCP)
		{
			eStatus = __MUTI_MB_TCP_DataRecv(object, &ucRcvAddress, &ucMBFrame, &usLength );
		}
		else 
		{
			 eStatus = __MUTI_MB_RTU_DataRecv(object, &ucRcvAddress, &ucMBFrame, &usLength );
			
			 if( ( ucRcvAddress != object->MB_id ) && ( ucRcvAddress != MUTI_MB_ADDRESS_BROADCAST ) )
			{
				goto MB_POLL_END;
			}
		}
		
		
		/* 2、 取出成功，进行解析 */   
        if( eStatus == MUTI_MB_ENOERR )
        {
			/* 取出功能码 */			
            object->function_code = ucMBFrame[MUTI_MB_PDU_FUNC_OFF];

            eException = MUTI_MB_EX_ILLEGAL_FUNCTION;

			/* 2.1、 取出成功，进行解析 */  
            for( i = 0; i < MUTI_MB_FUNC_HANDLERS_MAX; i++ )
            {
                /* No more function handlers registered. Abort. */
                if( xFuncHandlers[i].ucFunctionCode == 0 )//功能码等于0，抛弃
                {
                    break;
                }
                else if( xFuncHandlers[i].ucFunctionCode == object->function_code )//如果功能码和列表里的功能码匹配，就执行对应程序
                {
					
                    eException = xFuncHandlers[i].pxHandler(object, ucMBFrame, &usLength );
                    break;
                }
            }

            /* 2.2、进行回复 */
			 if( object->MB_id == MUTI_MB_TCP || ucRcvAddress != MUTI_MB_ADDRESS_BROADCAST )
            {
				if( eException != MUTI_MB_EX_NONE )
				{
					/* An exception occured. Build an error frame. */
					usLength = 0;
					ucMBFrame[usLength++] = ( uint8_t )( object->function_code | MUTI_MB_FUNC_ERROR );
					ucMBFrame[usLength++] = eException;
				}
					 
				if(object->MB_id == MUTI_MB_TCP)
				{
					 __MUTI_MB_TCPSend( object, ucMBFrame, usLength );
				}
				else 
				{
					__MUTI_MB_RTU_Send( object, ucMBFrame, usLength );
				}
			}         
        }
		
		MB_POLL_END:
		object->function_code = MUTI_MB_FUNC_NONE;
		object->state = IDLE;
    }
}


/************************内部函数*************************************************/



/***********************************
* MB_TCP API
***********************************/

/* 从接收缓冲区里 取得需要的数据 */
eMUTI_MB_ErrorCode
__MUTI_MB_TCP_DataRecv(MUTI_MB_object_t *object, UCHAR * no_used, UCHAR ** ppucFrame, USHORT * pusLength  )
{
    uint16_t        usPID;
    uint8_t         *pucMBTCPFrame;
    uint16_t        usLength;
    eMUTI_MB_ErrorCode    eStatus = MUTI_MB_EIO;

    pucMBTCPFrame = object->buf;

    usPID = pucMBTCPFrame[MB_TCP_PID] << 8U;
    usPID |= pucMBTCPFrame[MB_TCP_PID + 1];


    if( usPID == MB_TCP_PROTOCOL_ID )
    {
        *ppucFrame = &object->buf[MB_TCP_FUNC];
        *pusLength = object->len - MB_TCP_FUNC;
        eStatus = MUTI_MB_ENOERR;
    }
    else
    {
        eStatus = MUTI_MB_EIO;
    }
    return eStatus;

}




eMUTI_MB_ErrorCode
__MUTI_MB_TCPSend( MUTI_MB_object_t *object, const UCHAR * pucFrame, USHORT usLength )
{
    eMUTI_MB_ErrorCode    eStatus = MUTI_MB_ENOERR;
    UCHAR          *pucMBTCPFrame = ( UCHAR * ) pucFrame - MB_TCP_FUNC;
    USHORT          usTCPLength = usLength + MB_TCP_FUNC;

    /* The MBAP header is already initialized because the caller calls this
     * function with the buffer returned by the previous call. Therefore we 
     * only have to update the length in the header. Note that the length 
     * header includes the size of the Modbus PDU and the UID Byte. Therefore 
     * the length is usLength plus one.
     */
    pucMBTCPFrame[MB_TCP_LEN] = ( usLength + 1 ) >> 8U;
    pucMBTCPFrame[MB_TCP_LEN + 1] = ( usLength + 1 ) & 0xFF;


     object->send_cb(pucMBTCPFrame, usTCPLength );

    return eStatus;
}




/***********************************
* MB_RTU API
***********************************/

eMUTI_MB_ErrorCode
__MUTI_MB_RTU_DataRecv(MUTI_MB_object_t *object,  UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )
{
    UCHAR            xFrameReceived = FALSE;
    eMUTI_MB_ErrorCode    eStatus = MUTI_MB_ENOERR;

	
	// 关中断
    /* Length and CRC check */
    if( ( object->len >= MB_SER_PDU_SIZE_MIN )
        && ( usMBCRC16( ( UCHAR * ) object->buf,  object->len ) == 0 ) )
    {

        *pucRcvAddress = object->buf[MB_SER_PDU_ADDR_OFF];

        /* Total length of Modbus-PDU is Modbus-Serial-Line-PDU minus
         * size of address field and CRC checksum.
			Modbus-PDU总长度为Modbus-Serial-Line-PDU减去
			地址字段的大小和CRC校验和
         */
        *pusLength = ( USHORT )( object->len - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_CRC );

        /* Return the start of the Modbus PDU to the caller. */
        *pucFrame = ( UCHAR * ) & object->buf[MB_SER_PDU_PDU_OFF];
        xFrameReceived = TRUE;
    }
    else
    {
        eStatus = MUTI_MB_EIO;
    }

	// 开中断
		
    return eStatus;
}


eMUTI_MB_ErrorCode
__MUTI_MB_RTU_Send( MUTI_MB_object_t *object, const UCHAR * pucFrame, USHORT usLength )
{
	eMUTI_MB_ErrorCode    eStatus = MUTI_MB_ENOERR;
    UCHAR          *pucSndBufferCur = ( UCHAR * ) pucFrame;
	USHORT         usSndBufferCount = 0;
	USHORT          usCRC16;
	
	
	 /* First byte before the Modbus-PDU is the slave address. */
	pucSndBufferCur = ( UCHAR * ) pucFrame - 1;
	usSndBufferCount = 1;

	/* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
	pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = object->MB_id;
	usSndBufferCount += usLength;

	/* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */
	usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount );
	pucSndBufferCur[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );
	pucSndBufferCur[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );
	
	object->send_cb(pucSndBufferCur, usSndBufferCount );

    return eStatus;
}








/**将 回调函数返回的错误码 转换为期望错误码 **/
eMUTI_MB_Exception
__MUTI_MB_Error2Exception( eMUTI_MB_ErrorCode eErrorCode )
{
    eMUTI_MB_Exception    eStatus;

    switch ( eErrorCode )
    {
        case MUTI_MB_ENOERR:
            eStatus = MUTI_MB_EX_NONE;
            break;

        case MUTI_MB_ENOREG:
            eStatus = MUTI_MB_EX_ILLEGAL_DATA_ADDRESS;
            break;

        case MUTI_MB_ETIMEDOUT:
            eStatus = MUTI_MB_EX_SLAVE_BUSY;
            break;

        default:
            eStatus = MUTI_MB_EX_SLAVE_DEVICE_FAILURE;
            break;
    }

    return eStatus;
}








